package com.zxy.ziems.server.utils.tree;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * @author chdzq
 * @create 2022/11/21
 */
public class TreeUtils {

    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

    public static <T extends TreeInterface> List<T> tree(List<T> list) {
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        Map<String, T> map = list.stream().collect(Collectors.toMap(TreeInterface::_getTreeCode, ($0)->$0, throwingMerger(), LinkedHashMap::new));

        List<T> nodes = new ArrayList<>(0);
        for (T item: list) {
            String parentId = item._getTreeParentCode();
            //如果parentId为空或者取不到它的父节点就认为它是顶级节点
            if ((StrUtil.isBlank(parentId)) || Objects.isNull(map.get(parentId))) {
                List<T> children = item._getTreeChildren();
                //说明是顶级节点
                if (null == children) {
                    children = new ArrayList<>();
                }
                item._setTreeChildren(children);
                nodes.add(item);
            } else {
                //不是顶级节点，需要添加到父节点上
                T parent = map.get(parentId);
                List<T> parentChildren = parent._getTreeChildren();
                if (parentChildren == null) {
                    parentChildren = new ArrayList<>();
                }
                parentChildren.add(item);
                parent._setTreeChildren(parentChildren);
            }
        }
        return nodes;
    }

    public static <T extends TreeInterface> T strictTreeNode(List<T> list) {
        List<T> nodes = tree(list);
        if (nodes.isEmpty()) {
            return null;
        }
        Assert.isTrue(nodes.size() == 1, "树形结构序列化失败");
        return nodes.get(0);
    }
}
